package com.nightpiter.view;

import android.content.Context;
import android.graphics.Canvas;
import android.view.KeyEvent;
import android.view.View;

import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayController;
import com.google.android.maps.Point;
import com.google.android.maps.MapView.DeviceType;
import com.google.googlenav.map.MapPoint;
import com.google.googlenav.map.Zoom;
import com.nightpiter.model.objects.Coordinated;
import com.nightpiter.model.objects.Coordinates;
import com.nightpiter.view.Calculator.Mode;
import com.nightpiter.view.drawer.MetroDrawerWrapper;
import com.nightpiter.view.graphics.PointF;
import com.nightpiter.view.graphics.Rect;

/**
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
public class MetroOverlay extends Overlay implements Screen,
		MetroDrawerWrapper.RedawingListener {

	/**
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	public class LocationCalculator extends Calculator {

		private PixelCalculator pixelCalculator;

		private Point centerPoint;

		private int[] xyCoords = new int[2];

		/**
		 * private constructor
		 */
		private LocationCalculator() {
		}

		@Override
		public Mode getMode() {
			return Mode.LOCATION;
		}

		public void convert(Coordinates coordinates, PointF pointF) {
			pixelCalculator.getPointXY(new Point(coordinates.x, coordinates.y),
					xyCoords);
			pointF.x = xyCoords[0];
			pointF.y = xyCoords[1];
		}

		public void convert(Coordinates coordinates,
				com.nightpiter.view.graphics.Point point) {
			pixelCalculator.getPointXY(new Point(coordinates.x, coordinates.y),
					xyCoords);
			point.x = xyCoords[0];
			point.y = xyCoords[1];
		}

		@Override
		public void convert(Coordinated coordinated, PointF pointF) {
			convert(coordinated.getLocation(), pointF);
		}

		@Override
		public void convert(Coordinated coordinated,
				com.nightpiter.view.graphics.Point point) {
			convert(coordinated.getLocation(), point);
		}

		@Override
		public void convert(PointF coordF, PointF pointF) {
			pixelCalculator.getPointXY(
					new Point((int) coordF.x, (int) coordF.y), xyCoords);
			pointF.x = xyCoords[0];
			pointF.y = xyCoords[1];
		}

		@Override
		public void convert(PointF coordF,
				com.nightpiter.view.graphics.Point point) {
			pixelCalculator.getPointXY(
					new Point((int) coordF.x, (int) coordF.y), xyCoords);
			point.x = xyCoords[0];
			point.y = xyCoords[1];
		}

		/**
		 * this method does the same as fucking private Point
		 * MapView.getTapPoint(int x, int y);
		 */
		public void revert(PointF pointF, PointF coordF) {
			int[] centerXY = new int[2];
			pixelCalculator.getPointXY(centerPoint, centerXY);
			int dx = (int) (pointF.x - centerXY[0]);
			int dy = (int) (pointF.y - centerXY[1]);
			MapPoint shiftedPoint = new MapPoint(centerPoint.getLatitudeE6(),
					centerPoint.getLongitudeE6()).pixelOffset(dx, dy, Zoom
					.getZoom(pixelCalculator.getZoomLevel()));
			coordF.x = shiftedPoint.getLatitude();
			coordF.y = shiftedPoint.getLongitude();
		}

		@Override
		public void revert(com.nightpiter.view.graphics.Point point,
				PointF coordF) {
			int[] centerXY = new int[2];
			pixelCalculator.getPointXY(centerPoint, centerXY);
			int dx = point.x - centerXY[0];
			int dy = point.y - centerXY[1];
			MapPoint shiftedPoint = new MapPoint(centerPoint.getLatitudeE6(),
					centerPoint.getLongitudeE6()).pixelOffset(dx, dy, Zoom
					.getZoom(pixelCalculator.getZoomLevel()));
			coordF.x = shiftedPoint.getLatitude();
			coordF.y = shiftedPoint.getLongitude();
		}

		@Override
		public Coordinates getCoordinates(Coordinated coordinated) {
			return coordinated.getLocation();
		}

	}

	/**
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	public class MetroMapView extends MapView {

		/**
		 * @param context
		 */
		public MetroMapView(Context context) {
			super(context);
			this.setFocusable(true);
		}

		@Override
		public boolean onKeyDown(int keyCode, KeyEvent event) {
			switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_CENTER:
				MetroOverlay.this.resetToFit();
				invalidate();
				break;
			default:
				this.getController().onKey(this, keyCode, event);
				break;

			}
			return false;
		}

	}

	/** the subscriber that would be notified about some events */
	private Listener listener;

	/**
	 * The drawer used to analyze model and draw it's representation to the
	 * canvas
	 */
	private MetroDrawerWrapper drawer = MetroDrawerWrapper.getInstance();

	/** parent MapView */
	private MetroMapView mapView;

	private OverlayController overlayController;

	/** point-to-pixel converter */
	private LocationCalculator locationCalculator = new LocationCalculator();

	private int lastZoomLevel = 0;

	private boolean forceRedraw;

	/**
	 * 
	 * @param mapView
	 */
	public MetroOverlay(Context context) {
		this.mapView = new MetroMapView(context);
		overlayController = mapView.createOverlayController();
		overlayController.add(this, true);
	}

	@Override
	public void draw(Canvas canvas, PixelCalculator pixelCalculator,
			boolean shadow) {
		locationCalculator.pixelCalculator = pixelCalculator;
		locationCalculator.centerPoint = this.mapView.getMapCenter();

		if (pixelCalculator.getZoomLevel() != lastZoomLevel) {
			lastZoomLevel = pixelCalculator.getZoomLevel();
			drawer.draw(canvas, locationCalculator, true, this);
		} else {
			drawer.draw(canvas, locationCalculator, forceRedraw, this);
		}
		forceRedraw = false;

		super.draw(canvas, pixelCalculator, shadow);
	}

	public void onImageUpdated() {
		redraw(false);
	}

	@Override
	public boolean onTap(DeviceType deviceType, Point p,
			PixelCalculator calculator) {
		switch (deviceType) {
		case TOUCHSCREEN:
			locationCalculator.pixelCalculator = calculator;
			locationCalculator.centerPoint = this.mapView.getMapCenter();
			calculator.getPointXY(p, sXYCoords);
			listener.onStationRetrieved(drawer.searchStation(
					new com.nightpiter.view.graphics.Point(sXYCoords[0],
							sXYCoords[1]), locationCalculator));
			break;

		default:
			break;
		}
		return false;
	}

	public View getView() {
		return this.mapView;
	}

	public void prepare() {
		drawer.setSize(mapView.getWidth(), mapView.getHeight());

		resetToFit();

		redraw(false);
	}

	private void resetToFit() {
		Rect bounds = drawer.getBounds();
		Point center = new Point((bounds.getLeft() + bounds.getRight()) / 2,
				(bounds.getTop() + bounds.getBottom()) / 2);
		mapView.getController().centerMapTo(center, true);
		mapView.getController().zoomToSpan(
				bounds.getRight() - bounds.getLeft(),
				bounds.getBottom() - bounds.getTop());

	}

	public void redraw(boolean force) {
		this.forceRedraw = force;
		this.mapView.invalidate();
	}

	public void showZoom() {
		this.mapView.displayZoomDialog(60, 400);
	}

	public Mode getMode() {
		return locationCalculator.getMode();
	}

	/**
	 * subscribe to notifications
	 * 
	 * @param listener
	 *            the listener
	 */
	public void setListener(Listener listener) {
		if (listener != null) {
			this.listener = listener;
		}
	}

}
